package mt.dummy;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import mt.Order;
import mt.comm.ClienteSideMessageImp;
import mt.comm.ServerComm;
import mt.comm.ServerSideMessage;

public class ServerCommDummy implements ServerComm , Runnable {
	

	private static final int PORT = 8080;
	private ServerSocket serverSocket;
	private BlockingQueue<ServerSideMessage> queue = new LinkedBlockingQueue<ServerSideMessage>();
	private Map<String, OutputStream> clientes = new HashMap<String, OutputStream>();
	private Thread t;

	@Override
	public void start() {

		try {
			serverSocket = new ServerSocket(PORT);
			
		} catch (IOException e) {
			e.printStackTrace();
		}

		Thread listenning = new Thread(this);
		listenning.start();

	}

	/**
	 * Listen to communication and run another thread to receive the
	 * communication calling {@link #dealWhithMessage(Socket)}
	 * 
	 * @throws IOException
	 *             from problems closing a server socket
	 */

	protected void escuta() throws IOException {

		while (true) {
			final Socket socket = serverSocket.accept();
			new Thread(new Runnable() {

				@Override
				public void run() {

					dealWhithMessage(socket);

				}
			}).start();

		}
	}

	/**
	 * deal with a message receive from the client the first one is to connect
	 * the client to the server when the client is connect, is create a infinity
	 * loop to receive communication for the same client
	 * 
	 * @param socket2
	 *            - socket accept for the server socket
	 * @throws IOException
	 *             if somewhat the connection was close
	 * 
	 * @throws ClassNotFoundException
	 *             - {@link ServerSideMessage} class may not be found
	 */

	protected void dealWhithMessage(Socket socket2) {

		ServerSideMessage serverSideMessage = null;
		ObjectInputStream in = null;
		try {
			in = new ObjectInputStream(socket2.getInputStream());

			serverSideMessage = (ServerSideMessage) in.readObject();

			addConnection(socket2, serverSideMessage.getSenderNickname());
			adiciona(serverSideMessage);
			while (true) {

				serverSideMessage = (ServerSideMessage) in.readObject();

				adiciona(serverSideMessage);
			}

		} catch (IOException e) {
			if (serverSideMessage != null
					&& clientes.containsKey(serverSideMessage
							.getSenderNickname())) {
				clientes.remove(serverSideMessage.getSenderNickname());
			//	System.out.println("Disconnect "+ serverSideMessage.getSenderNickname());

				try {
					socket2.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

	}

	/**
	 * add client nickname and OutputStream to the HashMap
	 * @param socket2 socket open by the client
	 * @param senderNickname client nickname
	 * @throws IOException error in the connection
	 */
	private synchronized void addConnection(Socket socket2,
			String senderNickname) throws IOException {
		clientes.put(senderNickname, socket2.getOutputStream());
	}

	private synchronized void adiciona(ServerSideMessage serverSideMessage) {

		queue.add(serverSideMessage);

	}

	@Override
	public boolean hasNextMessage() {
		return !queue.isEmpty();
	}

	@Override
	public ServerSideMessage getNextMessage() {
		t = Thread.currentThread();

		try {
			return queue.take();
		} catch (InterruptedException e) {

			return null;
		}

	}

	@Override
	public void sendError(String toNickname, String error) {
		ClienteSideMessageImp clienteSideMessage = new ClienteSideMessageImp(error);

		try {
			OutputStream out = clientes.get(toNickname);
			new ObjectOutputStream(out).writeObject(clienteSideMessage);
			out.flush();
		} catch (IOException e) {
			clientes.remove(toNickname);
		//	System.out.println("disconnect " + toNickname);

		}

	}

	@Override
	public boolean clientIsConnected(String nickname) {

		return clientes.containsKey(nickname);
	}

	@Override
	public void disconnectClient(String nickname) {
		if (clientes.containsKey(nickname)) {
			try {

				clientes.get(nickname).close();
			} catch (IOException e) {
				//System.out.println("Cliente already unplugged");
e.printStackTrace();
			}
			clientes.remove(nickname);

		}
	}

	@Override
	public void sendOrder(String receiversNickname, Order order) {

		if (clientes.containsKey(receiversNickname)) {
		
			ClienteSideMessageImp clienteSideMessage = new ClienteSideMessageImp(order);

			try {
				OutputStream out = clientes.get(receiversNickname);
				new ObjectOutputStream(out).writeObject(clienteSideMessage);
				out.flush();
			} catch (IOException e) {
				clientes.remove(receiversNickname);
			//	System.out.println("unplugged " + receiversNickname);
e.printStackTrace();
			}
		}
	}

	@Override
	public void run() {

		try {
			escuta();
		} catch (IOException e) {

			interruptQueueThread();

		} finally {
			try {
				serverSocket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	/**
	 * interrupt the thread block in the queue when the severSocket close
	 */
	private void interruptQueueThread() {
		if (t != null) {
			t.interrupt();
		} else
			throw new IllegalStateException("Error in the beginning");
	}

}